package com.tianji.promotion.utils;

import com.tianji.common.exceptions.BizIllegalException;
import org.redisson.api.RLock;

/**
 * TODO:自定义锁策略的枚举类，提供了不同的锁获取策略。
 */
public enum MyLockStrategy {

    /**
     * 快速跳过策略。
     * 尝试立即获取锁，如果锁不可用，不等待直接返回结果。
     */
    SKIP_FAST() {
        /**
         * 尝试获取锁，不等待，直接返回结果。
         *
         * @param lock 要获取的锁对象
         * @param prop 锁的属性配置
         * @return 如果成功获取锁返回 true，否则返回 false
         * @throws InterruptedException 如果在等待过程中线程被中断
         */
        @Override
        public boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {
            // 尝试立即获取锁，不等待，若在指定的 leaseTime 内未释放，锁将自动释放
            return lock.tryLock(0, prop.leaseTime(), prop.unit());
        }
    },
    /**
     * 快速失败策略。
     * 尝试立即获取锁，如果锁不可用，抛出异常。
     */
    FAIL_FAST() {
        /**
         * 尝试获取锁，不等待，若失败则抛出异常。
         *
         * @param lock 要获取的锁对象
         * @param prop 锁的属性配置
         * @return 如果成功获取锁返回 true
         * @throws InterruptedException 如果在等待过程中线程被中断
         * @throws BizIllegalException  如果未能获取到锁，抛出业务异常
         */
        @Override
        public boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {
            // 尝试立即获取锁，不等待，若在指定的 leaseTime 内未释放，锁将自动释放
            boolean isLock = lock.tryLock(0, prop.leaseTime(), prop.unit());
            if (!isLock) {
                // 若未获取到锁，抛出业务异常提示请求太频繁
                throw new BizIllegalException("请求太频繁");
            }
            return true;
        }
    },
    /**
     * 持续尝试策略。
     * 持续尝试获取锁，直到成功获取。
     */
    KEEP_TRYING() {
        /**
         * 持续尝试获取锁，直到成功。
         *
         * @param lock 要获取的锁对象
         * @param prop 锁的属性配置
         * @return 总是返回 true，因为会一直尝试直到获取到锁
         * @throws InterruptedException 如果在等待过程中线程被中断
         */
        @Override
        public boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {
            // 一直尝试获取锁，直到成功，若在指定的 leaseTime 内未释放，锁将自动释放
            lock.lock(prop.leaseTime(), prop.unit());
            return true;
        }
    },
    /**
     * 重试超时后跳过策略。
     * 在指定的等待时间内尝试获取锁，如果超时仍未获取到，跳过操作。
     */
    SKIP_AFTER_RETRY_TIMEOUT() {
        /**
         * 在指定的等待时间内尝试获取锁，超时则跳过。
         *
         * @param lock 要获取的锁对象
         * @param prop 锁的属性配置
         * @return 如果成功获取锁返回 true，否则返回 false
         * @throws InterruptedException 如果在等待过程中线程被中断
         */
        @Override
        public boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {
            // 在指定的 waitTime 内尝试获取锁，若在指定的 leaseTime 内未释放，锁将自动释放
            return lock.tryLock(prop.waitTime(), prop.leaseTime(), prop.unit());
        }
    },
    /**
     * 重试超时后失败策略。
     * 在指定的等待时间内尝试获取锁，如果超时仍未获取到，抛出异常。
     */
    FAIL_AFTER_RETRY_TIMEOUT() {
        /**
         * 在指定的等待时间内尝试获取锁，超时则抛出异常。
         *
         * @param lock 要获取的锁对象
         * @param prop 锁的属性配置
         * @return 如果成功获取锁返回 true
         * @throws InterruptedException 如果在等待过程中线程被中断
         * @throws BizIllegalException  如果在指定时间内未能获取到锁，抛出业务异常
         */
        @Override
        public boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {
            // 在指定的 waitTime 内尝试获取锁，若在指定的 leaseTime 内未释放，锁将自动释放
            boolean isLock = lock.tryLock(prop.waitTime(), prop.leaseTime(), prop.unit());
            if (!isLock) {
                // 若在指定时间内未获取到锁，抛出业务异常提示请求太频繁
                throw new BizIllegalException("请求太频繁");
            }
            return true;
        }
    };

    /**
     * 抽象方法，用于尝试获取锁。
     * 每个枚举常量需要实现该方法以定义自己的锁获取策略。
     *
     * @param lock 要获取的锁对象
     * @param prop 锁的属性配置
     * @return 如果成功获取锁返回 true，否则返回 false
     * @throws InterruptedException 如果在等待过程中线程被中断
     */
    public abstract boolean tryLock(RLock lock, MyLock prop) throws InterruptedException;
}
